﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
// ReSharper disable RedundantSuppressNullableWarningExpression

namespace DimensionsHelper.Common.Metadata.Axis;

public sealed class AxisElementFactory
{
    private readonly AxisExpression _axis;

    internal AxisElementFactory(AxisExpression axis)
    {
        _axis = axis;
    }

    private string GetElementName(AxisElementType type, string? name)
    {
        if (string.IsNullOrEmpty(name))
        {
            return type + _axis.ElementsCount[(int)type].ToString();
        }

        if (_axis.NameToNode.ContainsKey(name!))
        {
            throw new Exception($"名为'{name}'的轴元素已存在。");
        }

        return name!;
    }


    /// <summary>
    ///     创建一个分类值轴元素，语法：[^]Category_Name['Description'][[Suffixes ...]]
    /// </summary>
    /// <param name="category">分类元素名</param>
    /// <param name="description">分类元素描述，可忽略</param>
    public static AxisElement CreateCategory(string category, string? description = null)
    {
        Debug.Assert(!string.IsNullOrEmpty(category));

        return new AxisElement(AxisElementType.Category)
        {
            Name = category,
            Description = description
        };
    }


    /// <summary>
    ///     创建一个分类区间轴元素，语法：[^][Lower]..[Upper]。
    /// </summary>
    /// <param name="lower">区间左值</param>
    /// <param name="upper">区间右值</param>
    public static AxisElement CreateCategoryRange(string? lower = null, string? upper = null)
    {
        var element = new AxisElement(AxisElementType.CategoryRange)
        {
            Name = string.Empty
        };

        if (!string.IsNullOrEmpty(lower))
        {
            element.LowerBound = lower!;
        }

        if (!string.IsNullOrEmpty(upper))
        {
            element.UpperBound = upper!;
        }

        return element;
    }


    /// <summary>
    ///     创建一个新的文本行轴元素，语法：Text_Name ['Description'] text()
    /// </summary>
    /// <param name="name">轴元素名称</param>
    /// <param name="description">轴元素描述，可忽略</param>
    public AxisElement CreateText(string? name = null, string? description = null)
    {
        return CreateParameterlessElement(AxisElementType.Text, name, description);
    }


    /// <summary>
    ///     创建一个轴基数元素，语法 Base_Name ['Description'] base(['Expression']) [[Suffixes ...]]
    /// </summary>
    /// <param name="name">元素名</param>
    /// <param name="description">元素描述，可忽略</param>
    /// <param name="expr">基数筛选表达式，可忽略</param>
    public AxisElement CreateBase(string name, string? description = null, string? expr = null)
    {
        return CreateSingleOptionalStringParameterElement(AxisElementType.Base, name, description, expr);
    }


    /// <summary>
    ///     创建一个轴未加权的基数元素，语法 Base_Name ['Description'] unweightedbase(['Expression']) [[Suffixes ...]]
    /// </summary>
    /// <param name="name">元素名</param>
    /// <param name="description">元素描述，可忽略</param>
    /// <param name="expr">未加权的基数筛选表达式，可忽略</param>
    public AxisElement CreateUnweightedBase(string? name, string? description = null, string? expr = null)
    {
        return CreateSingleOptionalStringParameterElement(AxisElementType.UnweightedBase, name, description, expr);
    }


    /// <summary>
    ///     创建一个轴有效基数元素，语法 Base_Name ['Description'] effectivebase() [[Suffixes ...]]
    /// </summary>
    /// <param name="name">元素名</param>
    /// <param name="description">元素描述，可忽略</param>
    public AxisElement CreateEffectiveBase(string? name, string? description = null)
    {
        return CreateParameterlessElement(AxisElementType.EffectiveBase, name, description);
    }


    /// <summary>
    ///     创建一个轴表达式元素，语法：Expression_Name ['Description'] expression('expression_text') [[Suffixes ...]]
    /// </summary>
    /// <param name="name">元素名</param>
    /// <param name="description">元素描述，可忽略</param>
    /// <param name="expression">表达式文本，不可忽略</param>
    public AxisElement CreateExpression(string name, string description, string expression)
    {
        Debug.Assert(!string.IsNullOrEmpty(expression));
        return CreateSingleOptionalStringParameterElement(AxisElementType.Expression, name, description,
            expression);
    }


    private AxisElement CreateParameterlessElement(AxisElementType type,
        string? name = null, string? description = null)
    {
        return new AxisElement(type)
        {
            Name = GetElementName(type, name),
            Description = description
        };
    }


    private AxisElement CreateSingleOptionalStringParameterElement(AxisElementType type,
        string? name = null, string? description = null, string? argument = null)
    {
        var element = new AxisElement(type)
        {
            Name = GetElementName(type, name),
            Description = description
        };

        if (!string.IsNullOrEmpty(argument))
        {
            element.AddParameter(AxisElementParameterType.String, argument!);
        }

        return element;
    }


    private AxisElement CreateElementListParameterElement(AxisElementType type,
        string? name = null, string? description = null, IEnumerable<AxisElement>? elements = null)
    {
        var element = new AxisElement(type)
        {
            Name = GetElementName(type, name),
            Description = description
        };

        if (elements != null)
        {
            foreach (AxisElement item in elements)
            {
                element.AddParameter(AxisElementParameterType.Element, item);
            }
        }

        return element;
    }


    private AxisElement CreateMeanLike(AxisElementType type,
        string? name, string? description = null, string? varName = null, string? expr = null)
    {
        var element = new AxisElement(type)
        {
            Name = GetElementName(type, name),
            Description = description
        };

        if (!string.IsNullOrEmpty(varName))
        {
            element.AddParameter(AxisElementParameterType.Name, varName!);
        }

        if (!string.IsNullOrEmpty(expr))
        {
            element.AddParameter(AxisElementParameterType.String, expr!);
        }

        return element;
    }


    /// <summary>
    ///     创建一个轴均值元素，语法：element_Name ['description'] mean([var_name][ ,'expression_text']) [[Suffixes ...]]
    /// </summary>
    /// <param name="name">轴元素名</param>
    /// <param name="description">轴元素描述</param>
    /// <param name="varName">计算变量名</param>
    /// <param name="expr">均值计算筛选表达式</param>
    public AxisElement CreateMean(
        string? name = null, string? description = null, string? varName = null, string? expr = null)
    {
        return CreateMeanLike(AxisElementType.Mean, name, description, varName, expr);
    }


    /// <summary>
    ///     创建一个轴标准偏差元素，语法：element_Name ['description'] stddev([var_name][ ,'expression_text']) [[Suffixes ...]]
    /// </summary>
    /// <param name="name">轴元素名</param>
    /// <param name="description">轴元素描述</param>
    /// <param name="varName">计算变量名</param>
    /// <param name="expr">标准偏差计算筛选表达式</param>
    public AxisElement CreateStdDev(
        string? name = null, string? description = null, string? varName = null, string? expr = null)
    {
        return CreateMeanLike(AxisElementType.StdDev, name, description, varName, expr);
    }


    /// <summary>
    ///     创建一个轴标准误差元素，语法：element_Name ['description'] stderr([var_name][ ,'expression_text']) [[Suffixes ...]]
    /// </summary>
    /// <param name="name">轴元素名</param>
    /// <param name="description">轴元素描述</param>
    /// <param name="varName">计算变量名</param>
    /// <param name="expr">标准误差计算筛选表达式</param>
    public AxisElement CreateStdErr(
        string? name = null, string? description = null, string? varName = null, string? expr = null)
    {
        return CreateMeanLike(AxisElementType.StdErr, name, description, varName, expr);
    }


    /// <summary>
    ///     创建一个轴样本方差元素，语法：element_Name ['description'] sampvar([var_name][ ,'expression_text']) [[Suffixes ...]]
    /// </summary>
    /// <param name="name">轴元素名</param>
    /// <param name="description">轴元素描述</param>
    /// <param name="varName">计算变量名</param>
    /// <param name="expr">样本方差计算筛选表达式</param>
    public AxisElement CreateSampVar(
        string? name = null, string? description = null, string? varName = null, string? expr = null)
    {
        return CreateMeanLike(AxisElementType.SampVar, name, description, varName, expr);
    }


    /// <summary>
    ///     创建一个轴最小值元素，语法：element_Name ['description'] min([var_name][ ,'expression_text']) [[Suffixes ...]]
    /// </summary>
    /// <param name="name">轴元素名</param>
    /// <param name="description">轴元素描述</param>
    /// <param name="varName">计算变量名</param>
    /// <param name="expr">最小值计算筛选表达式</param>
    public AxisElement CreateMin(
        string? name = null, string? description = null, string? varName = null, string? expr = null)
    {
        return CreateMeanLike(AxisElementType.Min, name, description, varName, expr);
    }


    /// <summary>
    ///     创建一个轴最大值元素，语法：element_Name ['description'] max([var_name][ ,'expression_text']) [[Suffixes ...]]
    /// </summary>
    /// <param name="name">轴元素名</param>
    /// <param name="description">轴元素描述</param>
    /// <param name="varName">计算变量名</param>
    /// <param name="expr">最大值计算筛选表达式</param>
    public AxisElement CreateMax(
        string? name = null, string? description = null, string? varName = null, string? expr = null)
    {
        return CreateMeanLike(AxisElementType.Max, name, description, varName, expr);
    }


    /// <summary>
    ///     创建一个轴众数元素，语法：element_Name ['description'] mode([var_name][ ,'expression_text']) [[Suffixes ...]]
    /// </summary>
    /// <param name="name">轴元素名</param>
    /// <param name="description">轴元素描述</param>
    /// <param name="varName">计算变量名</param>
    /// <param name="expr">众数计算筛选表达式</param>
    public AxisElement CreateMode(
        string? name = null, string? description = null, string? varName = null, string? expr = null)
    {
        return CreateMeanLike(AxisElementType.Mode, name, description, varName, expr);
    }


    /// <summary>
    ///     创建一个轴中位数元素，语法：element_Name ['description'] median([var_name][ ,'expression_text']) [[Suffixes ...]]
    /// </summary>
    /// <param name="name">轴元素名</param>
    /// <param name="description">轴元素描述</param>
    /// <param name="varName">计算变量名</param>
    /// <param name="expr">中位数计算筛选表达式</param>
    public AxisElement CreateMedian(
        string? name = null, string? description = null, string? varName = null, string? expr = null)
    {
        return CreateMeanLike(AxisElementType.Median, name, description, varName, expr);
    }


    /// <summary>
    ///     创建一个轴求和数元素，语法：element_Name ['description'] sum([var_name][ ,'expression_text']) [[Suffixes ...]]
    /// </summary>
    /// <param name="name">轴元素名</param>
    /// <param name="description">轴元素描述</param>
    /// <param name="varName">计算变量名</param>
    /// <param name="expr">求和计算筛选表达式</param>
    public AxisElement CreateSum(
        string? name = null, string? description = null, string? varName = null, string? expr = null)
    {
        return CreateMeanLike(AxisElementType.Sum, name, description, varName, expr);
    }


    /// <summary>
    ///     创建一个轴小计元素，语法：element_Name ['description'] subtotal() [[Suffixes ...]]
    /// </summary>
    /// <param name="name">轴元素名</param>
    /// <param name="description">轴元素描述</param>
    public AxisElement CreateSubTotal(string? name = null, string? description = null)
    {
        return CreateParameterlessElement(AxisElementType.SubTotal, name, description);
    }


    /// <summary>
    ///     创建一个轴总计元素，语法：element_Name ['description'] total() [[Suffixes ...]]
    /// </summary>
    /// <param name="name">轴元素名</param>
    /// <param name="description">轴元素描述</param>
    public AxisElement CreateTotal(string? name = null, string? description = null)
    {
        return CreateParameterlessElement(AxisElementType.Total, name, description);
    }


    /// <summary>
    ///     创建一个轴Net元素，语法：element_name ['description'] net({[element_list]}) [[suffixes ...]]
    /// </summary>
    /// <param name="name">轴元素名</param>
    /// <param name="description">轴元素描述</param>
    /// <param name="elements">子元素列表</param>
    public AxisElement CreateNet(
        string? name = null, string? description = null, IEnumerable<AxisElement>? elements = null)
    {
        return CreateElementListParameterElement(AxisElementType.Net, name, description, elements);
    }


    /// <summary>
    ///     创建一个轴Combine元素，语法：element_name ['description'] combine({[element_list]}) [[suffixes ...]]
    /// </summary>
    /// <param name="name">轴元素名</param>
    /// <param name="description">轴元素描述</param>
    /// <param name="elements">子元素列表</param>
    public AxisElement CreateCombine(
        string? name = null, string? description = null, IEnumerable<AxisElement>? elements = null)
    {
        return CreateElementListParameterElement(AxisElementType.Combine, name, description, elements);
    }


    /// <summary>
    ///     创建一个轴百分位数元素，语法：element_name ['description'] percentile(var_name[, cutOffValue[, filter_expression]]) [[suffixes
    ///     ...]]
    /// </summary>
    /// <param name="name">轴元素名</param>
    /// <param name="description">轴元素描述</param>
    /// <param name="varName">求值使用的数值变量名</param>
    /// <param name="cutOffValue">百分位数</param>
    /// <param name="filter">样本筛选器表达式</param>
    public AxisElement CreatePercentile(
        string name, string description, 
        string varName, 
        double cutOffValue = 50d,
        string? filter = null)
    {
        Debug.Assert(!string.IsNullOrEmpty(varName));

        var percentile = new AxisElement(AxisElementType.Percentile)
        {
            Name = GetElementName(AxisElementType.Percentile, name),
            Description = description
        };

        percentile.AddParameter(AxisElementParameterType.Name, varName);
        percentile.AddParameter(AxisElementParameterType.Number, cutOffValue);

        if (!string.IsNullOrEmpty(filter))
        {
            percentile.AddParameter(AxisElementParameterType.String, filter!);
        }

        return percentile;
    }


    /// <summary>
    ///     创建一个轴ntd元素，语法：element_name ['description'] ntd() [[suffixes ...]]
    /// </summary>
    /// <param name="name">轴元素名</param>
    /// <param name="description">轴元素描述</param>
    public AxisElement CreateNtd(string? name = null, string? description = null)
    {
        return CreateParameterlessElement(AxisElementType.Ntd, name, description);
    }


    /// <summary>
    ///     创建一个轴ppt元素，语法：element_name ['description'] ppt() [[suffixes ...]]
    /// </summary>
    /// <param name="name">轴元素名</param>
    /// <param name="description">轴元素描述</param>
    public AxisElement CreatePpt(string? name = null, string? description = null)
    {
        return CreateParameterlessElement(AxisElementType.Ppt, name, description);
    }
}